﻿// Copyright 2012 Jack Bulan (agentgeo)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using GemWorkshop.Formats.Nitro;

namespace GemWorkshop.Pokemon
{
    public class PokemonLoader
    {
        #region Names
        private readonly string[] POKEMON_NAMES = {
                                                  "????????","Bulbasaur","Ivysaur","Venusaur","Charmander","Charmeleon","Charizard","Squirtle","Wartortle","Blastoise","Caterpie","Metapod","Butterfree","Weedle","Kakuna","Beedrill","Pidgey","Pidgeotto","Pidgeot","Rattata","Raticate","Spearow","Fearow","Ekans","Arbok","Pikachu","Raichu","Sandshrew","Sandslash","Nidoran-F","Nidorina","Nidoqueen","Nidoran-M","Nidorino","Nidoking","Clefairy","Clefable","Vulpix","Ninetales","Jigglypuff","Wigglytuff","Zubat","Golbat","Oddish","Gloom","Vileplume","Paras","Parasect","Venonat","Venomoth","Diglett","Dugtrio","Meowth","Persian","Psyduck","Golduck","Mankey","Primeape","Growlithe","Arcanine","Poliwag","Poliwhirl","Poliwrath","Abra","Kadabra","Alakazam","Machop","Machoke","Machamp","Bellsprout","Weepinbell","Victreebel","Tentacool","Tentacruel","Geodude","Graveler","Golem","Ponyta","Rapidash","Slowpoke","Slowbro","Magnemite","Magneton","Farfetch'd","Doduo","Dodrio","Seel","Dewgong","Grimer","Muk","Shellder","Cloyster","Gastly","Haunter","Gengar","Onix","Drowzee","Hypno","Krabby","Kingler","Voltorb","Electrode","Exeggcute","Exeggutor","Cubone","Marowak","Hitmonlee","Hitmonchan","Lickitung","Koffing","Weezing","Rhyhorn","Rhydon","Chansey","Tangela","Kangaskhan","Horsea","Seadra","Goldeen","Seaking","Staryu","Starmie","Mr.Mime","Scyther","Jynx","Electabuzz","Magmar","Pinsir","Tauros","Magikarp","Gyarados","Lapras","Ditto","Eevee","Vaporeon","Jolteon","Flareon","Porygon","Omanyte","Omastar","Kabuto","Kabutops","Aerodactyl","Snorlax","Articuno","Zapdos","Moltres","Dratini","Dragonair","Dragonite","Mewtwo","Mew","Chikorita","Bayleef","Meganium","Cyndaquil","Quilava","Typhlosion","Totodile","Croconaw","Feraligatr","Sentret","Furret","Hoothoot","Noctowl","Ledyba","Ledian","Spinarak","Ariados","Crobat","Chinchou","Lanturn","Pichu","Cleffa","Igglybuff","Togepi","Togetic","Natu","Xatu","Mareep","Flaaffy","Ampharos","Bellossom","Marill","Azumarill","Sudowoodo","Politoed","Hoppip","Skiploom","Jumpluff","Aipom","Sunkern","Sunflora","Yanma","Wooper","Quagsire","Espeon","Umbreon","Murkrow","Slowking","Misdreavus","Unown","Wobbuffet","Girafarig","Pineco","Forretress","Dunsparce","Gligar","Steelix","Snubbull","Granbull","Qwilfish","Scizor","Shuckle","Heracross","Sneasel","Teddiursa","Ursaring","Slugma","Magcargo","Swinub","Piloswine","Corsola","Remoraid","Octillery","Delibird","Mantine","Skarmory","Houndour","Houndoom","Kingdra","Phanpy","Donphan","Porygon2","Stantler","Smeargle","Tyrogue","Hitmontop","Smoochum","Elekid","Magby","Miltank","Blissey","Raikou","Entei","Suicune","Larvitar","Pupitar","Tyranitar","Lugia","Ho-oh","Celebi","Treecko","Grovyle","Sceptile","Torchic","Combusken","Blaziken","Mudkip","Marshtomp","Swampert","Poochyena","Mightyena","Zigzagoon","Linoone","Wurmple","Silcoon","Beautifly","Cascoon","Dustox","Lotad","Lombre","Ludicolo","Seedot","Nuzleaf","Shiftry","Taillow","Swellow","Wingull","Pelipper","Ralts","Kirlia","Gardevoir","Surskit","Masquerain","Shroomish","Breloom","Slakoth","Vigoroth","Slaking","Nincada","Ninjask","Shedinja","Whismur","Loudred","Exploud","Makuhita","Hariyama","Azurill","Nosepass","Skitty","Delcatty","Sableye","Mawile","Aron","Lairon","Aggron","Meditite","Medicham","Electrike","Manectric","Plusle","Minun","Volbeat","Illumise","Roselia","Gulpin","Swalot","Carvanha","Sharpedo","Wailmer","Wailord","Numel","Camerupt","Torkoal","Spoink","Grumpig","Spinda","Trapinch","Vibrava","Flygon","Cacnea","Cacturne","Swablu","Altaria","Zangoose","Seviper","Lunatone","Solrock","Barboach","Whiscash","Corphish","Crawdaunt","Baltoy","Claydol","Lileep","Cradily","Anorith","Armaldo","Feebas","Milotic","Castform","Kecleon","Shuppet","Banette","Duskull","Dusclops","Tropius","Chimecho","Absol","Wynaut","Snorunt","Glalie","Spheal","Sealeo","Walrein","Clamperl","Huntail","Gorebyss","Relicanth","Luvdisc","Bagon","Shellgon","Salamence","Beldum","Metang","Metagross","Regirock","Regice","Registeel","Latias","Latios","Kyogre","Groudon","Rayquaza","Jirachi","Deoxys","Turtwig","Grotle","Torterra","Chimchar","Monferno","Infernape","Piplup","Prinplup","Empoleon","Starly","Staravia","Staraptor","Bidoof","Bibarel","Kricketot","Kricketune","Shinx","Luxio","Luxray","Budew","Roserade","Cranidos","Rampardos","Shieldon","Bastiodon","Burmy","Wormadam","Mothim","Combee","Vespiquen","Pachirisu","Buizel","Floatzel","Cherubi","Cherrim","Shellos","Gastrodon","Ambipom","Drifloon","Drifblim","Buneary","Lopunny","Mismagius","Honchkrow","Glameow","Purugly","Chingling","Stunky","Skuntank","Bronzor","Bronzong","Bonsly","MimeJr.","Happiny","Chatot","Spiritomb","Gible","Gabite","Garchomp","Munchlax","Riolu","Lucario","Hippopotas","Hippowdon","Skorupi","Drapion","Croagunk","Toxicroak","Carnivine","Finneon","Lumineon","Mantyke","Snover","Abomasnow","Weavile","Magnezone","Lickilicky","Rhyperior","Tangrowth","Electivire","Magmortar","Togekiss","Yanmega","Leafeon","Glaceon","Gliscor","Mamoswine","Porygon-Z","Gallade","Probopass","Dusknoir","Froslass","Rotom","Uxie","Mesprit","Azelf","Dialga","Palkia","Heatran","Regigigas","Giratina","Cresselia","Phione","Manaphy","Darkrai","Shaymin","Arceus"
                                              };

        private readonly string[] MOVE_NAMES = {
                                                  "None","Pound","Karate Chop","DoubleSlap","Comet Punch","Mega Punch","Pay Day","Fire Punch","Ice Punch","ThunderPunch","Scratch","ViceGrip","Guillotine","Razor Wind","Swords Dance","Cut","Gust","Wing Attack","Whirlwind","Fly","Bind","Slam","Vine Whip","Stomp","Double Kick","Mega Kick","Jump Kick","Rolling Kick","Sand-Attack","Headbutt","Horn Attack","Fury Attack","Horn Drill","Tackle","Body Slam","Wrap","Take Down","Thrash","Double-Edge","Tail Whip","Poison Sting","Twineedle","Pin Missile","Leer","Bite","Growl","Roar","Sing","Supersonic","SonicBoom","Disable","Acid","Ember","Flamethrower","Mist","Water Gun","Hydro Pump","Surf","Ice Beam","Blizzard","Psybeam","BubbleBeam","Aurora Beam","Hyper Beam","Peck","Drill Peck","Submission","Low Kick","Counter","Seismic Toss","Strength","Absorb","Mega Drain","Leech Seed","Growth","Razor Leaf","SolarBeam","PoisonPowder","Stun Spore","Sleep Powder","Petal Dance","String Shot","Dragon Rage","Fire Spin","ThunderShock","Thunderbolt","Thunder Wave","Thunder","Rock Throw","Earthquake","Fissure","Dig","Toxic","Confusion","Psychic","Hypnosis","Meditate","Agility","Quick Attack","Rage","Teleport","Night Shade","Mimic","Screech","Double Team","Recover","Harden","Minimize","SmokeScreen","Confuse Ray","Withdraw","Defense Curl","Barrier","Light Screen","Haze","Reflect","Focus Energy","Bide","Metronome","Mirror Move","Selfdestruct","Egg Bomb","Lick","Smog","Sludge","Bone Club","Fire Blast","Waterfall","Clamp","Swift","Skull Bash","Spike Cannon","Constrict","Amnesia","Kinesis","Softboiled","Hi Jump Kick","Glare","Dream Eater","Poison Gas","Barrage","Leech Life","Lovely Kiss","Sky Attack","Transform","Bubble","Dizzy Punch","Spore","Flash","Psywave","Splash","Acid Armor","Crabhammer","Explosion","Fury Swipes","Bonemerang","Rest","Rock Slide","Hyper Fang","Sharpen","Conversion","Tri Attack","Super Fang","Slash","Substitute","Struggle","Sketch","Triple Kick","Thief","Spider Web","Mind Reader","Nightmare","Flame Wheel","Snore","Curse","Flail","Conversion ","Aeroblast","Cotton Spore","Reversal","Spite","Powder Snow","Protect","Mach Punch","Scary Face","Faint Attack","Sweet Kiss","Belly Drum","Sludge Bomb","Mud-Slap","Octazooka","Spikes","Zap Cannon","Foresight","Destiny Bond","Perish Song","Icy Wind","Detect","Bone Rush","Lock-On","Outrage","Sandstorm","Giga Drain","Endure","Charm","Rollout","False Swipe","Swagger","Milk Drink","Spark","Fury Cutter","Steel Wing","Mean Look","Attract","Sleep Talk","Heal Bell","Return","Present","Frustration","Safeguard","Pain Split","Sacred Fire","Magnitude","DynamicPunch","Megahorn","DragonBreath","Baton Pass","Encore","Pursuit","Rapid Spin","Sweet Scent","Iron Tail","Metal Claw","Vital Throw","Morning Sun","Synthesis","Moonlight","Hidden Power","Cross Chop","Twister","Rain Dance","Sunny Day","Crunch","Mirror Coat","Psych Up","ExtremeSpeed","AncientPower","Shadow Ball","Future Sight","Rock Smash","Whirlpool","Beat Up","Fake Out","Uproar","Stockpile","Spit Up","Swallow","Heat Wave","Hail","Torment","Flatter","Will-O-Wisp","Memento","Facade","Focus Punch","SmellingSalt","Follow Me","Nature Power","Charge","Taunt","Helping Hand","Trick","Role Play","Wish","Assist","Ingrain","Superpower","Magic Coat","Recycle","Revenge","Brick Break","Yawn","Knock Off","Endeavor","Eruption","Skill Swap","Imprison","Refresh","Grudge","Snatch","Secret Power","Dive","Arm Thrust","Camouflage","Tail Glow","Luster Purge","Mist Ball","FeatherDance","Teeter Dance","Blaze Kick","Mud Sport","Ice Ball","Needle Arm","Slack Off","Hyper Voice","Poison Fang","Crush Claw","Blast Burn","Hydro Cannon","Meteor Mash","Astonish","Weather Ball","Aromatherapy","Fake Tears","Air Cutter","Overheat","Odor Sleuth","Rock Tomb","Silver Wind","Metal Sound","GrassWhistle","Tickle","Cosmic Power","Water Spout","Signal Beam","Shadow Punch","Extrasensory","Sky Uppercut","Sand Tomb","Sheer Cold","Muddy Water","Bullet Seed","Aerial Ace","Icicle Spear","Iron Defense","Block","Howl","Dragon Claw","Frenzy Plant","Bulk Up","Bounce","Mud Shot","Poison Tail","Covet","Volt Tackle","Magical Leaf","Water Sport","Calm Mind","Leaf Blade","Dragon Dance","Rock Blast","Shock Wave","Water Pulse","Doom Desire","???","Roost","Gravity","Miracle Eye","Wake Up Slap","Hammer Arm","Gyro Ball","Healing Wish","Brine","Natural Gift","Feint","Pluck","Tail Wind","Acupressure","Metal Burst","U-Turn","Close Combat","Payback","Assurance","Embargo","Fling","Psycho Shift","Trump Card","Heal Block","Wring Out","Power Trick","Gastro Acid","Lucky Chant","Me First","Copycat","Power Swap","Guard Swap","Punishment","Last Resort","Worry Seed","Shadow Claw","Toxic Spikes","Heart Swap","Aqua Ring","Magnet Rise","Flare Blitz","Force Palm","Aura sphere","Rock Polish","Poison Jab","Dark Pulse","Night Slash","Aqua Tail","Seedbomb","Air Slash","X-Scissor","Bug Buzz","Dragon Pulse","Dragon Rush","Power Gem","Drain Punch","Vacuum Wave","Focus Blast","Energy Ball","Brave Bird","Earth Power","Switcheroo","Giga Impact","Nasty Plot","Bullet Punch","Avalanche","Ice Shard","Shadow Claw","Thunder Fang","Ice Fang","Fire Fang","Shadow Sneak","Mud Bomb","Psycho Cut","Zen Headbutt","Mirror Shot","Flash Cannon","Rock Climb","Defog","Trick Room","Draco Meteor","Discharge","Lava Plume","Leaf Storm","Power Whip","Rock Wrecker","Cross Poison","Gunk Shot","Iron Head","Magnet Bomb","Stone Edge","Captivate","Stealth Rock","Grass Knot","Chatter","Judgement","Bug Bite","Charge Beam","Wood Hammer","Aqua Jet","Attack Order","Defend Order","Heal Order","Head Smash","Double Hit","Roar of Time","Spacial Rend","Lunar Dance","Crush Grip","Magma Storm","Dark Void","Seed Flare","Ominous Wind","Shadow Force"
                                             };
        #endregion

        private const uint BSTATS_LENGTH = 0x30;

        private Narc bn, en, mn;
        public BaseStats baseStats;
        public Evolution[] evolutions;
        public Move[] moveset;

        public PokemonLoader(Narc baseNarc, Narc evoNarc, 
            Narc movesNarc)
        {
            bn = baseNarc;
            en = evoNarc;
            mn = movesNarc;
        }

        public void LoadPokemon(int poke)
        {
            // There's only one file in the narc, anyways...
            BinaryReader br = new BinaryReader(bn.ExtractStream((uint)poke));
            LoadBaseStats(br);
            br.Close();
            br.Dispose();

            // Load the evolutions
            br = new BinaryReader(en.ExtractStream((uint)poke));
            LoadEvolutions(br);
            br.Close();
            br.Dispose();

            br = new BinaryReader(mn.ExtractStream((uint)poke));
            LoadMoveset(br);
            br.Close();
            br.Dispose();
        }

        private void LoadBaseStats(BinaryReader br)
        {
            baseStats = new BaseStats();

            baseStats.HP = br.ReadByte();
            baseStats.Attack = br.ReadByte();
            baseStats.Defense = br.ReadByte();
            baseStats.Speed = br.ReadByte();
            baseStats.SpAttack = br.ReadByte();
            baseStats.SpDefense = br.ReadByte();

            baseStats.Type1 = br.ReadByte();
            baseStats.Type2 = br.ReadByte();

            baseStats.CatchRate = br.ReadByte();
            baseStats.BaseXp = br.ReadByte();

            baseStats.EffortYield = br.ReadUInt16();

            baseStats.EVHp = (byte)(baseStats.EffortYield & 3);
            baseStats.EVAttack = (byte)(baseStats.EffortYield >> 2 & 3);
            baseStats.EVDefense = (byte)(baseStats.EffortYield >> 4 & 3);
            baseStats.EVSpeed = (byte)(baseStats.EffortYield >> 6 & 3);
            baseStats.EVSpAttack = (byte)(baseStats.EffortYield >> 8 & 3);
            baseStats.EVSpDefense = (byte)(baseStats.EffortYield >> 10 & 3);

            //baseStats.EffortYield2 = br.ReadByte();

            baseStats.Item1 = br.ReadUInt16();
            baseStats.Item2 = br.ReadUInt16();

            baseStats.GenderRatio = br.ReadByte();
            if (baseStats.GenderRatio == 255) baseStats.HasGenders = false;
            else baseStats.HasGenders = true;

            baseStats.EggCycles = br.ReadByte();
            baseStats.BaseHappiness = br.ReadByte();
            baseStats.XpType = br.ReadByte();

            baseStats.EggType1 = br.ReadByte();
            baseStats.EggType2 = br.ReadByte();

            baseStats.Ability1 = br.ReadByte();
            baseStats.Ability2 = br.ReadByte();

            baseStats.SafariRunRate = br.ReadByte();
            baseStats.DexColor = br.ReadByte();

            br.BaseStream.Seek(2L, SeekOrigin.Current);

            baseStats.TMSwitches = br.ReadBytes(13);

            br.BaseStream.Seek(3L, SeekOrigin.Current);
        }

        private void LoadEvolutions(BinaryReader br)
        {
            // Format:
            // [ type ] [ param ] [ target ]

            evolutions = new Evolution[7];
            for (int evo = 0; evo < 7; evo++)
            {
                evolutions[evo].Type = br.ReadUInt16();
                evolutions[evo].Parameter = br.ReadUInt16();
                evolutions[evo].Target = br.ReadUInt16();
            }
        }

        private void LoadMoveset(BinaryReader br)
        {
            List<Move> moves = new List<Move>();
            while (br.BaseStream.Position < br.BaseStream.Length)
            {
                Move m = new Move();
                ushort val = br.ReadUInt16();

                if (val == 0xFFFF) break; // Signifies terminate
                // END = FF FF 00 00

                m.Value = (ushort)(val & 0x1FF); // First 9 bits
                m.Level = (ushort)(val >> 9 & 0x7F); // Last 7 bits

                moves.Add(m);
            }
            moveset = moves.ToArray();
        }

        public string GetName(int index)
        {
            if (index < POKEMON_NAMES.Length)
                return POKEMON_NAMES[index];
            else
                return "????????";
        }

        public string GetEvolutionName(ushort type)
        {
            switch (type)
            {
                case 0: return "None";
                case 1: return "Happiness";
                case 2: return "Happiness Day";
                case 3: return "Happiness Night";
                case 4: return "Level";
                case 5: return "Trade";
                case 6: return "Trade w/ Item";
                case 7: return "Item";
                case 8: return "Level w/ Attack > Defense";
                case 9: return "Level w/ Attack = Defense";
                case 10: return "Level w/ Attack < Defense";
                case 11: return "Level w/ Personality <= 4";
                case 12: return "Level w/ Personality >= 5";
                case 13: return "Level (Ninjask-style)";
                case 14: return "Level w/ Create (Shedinja-style)";
                case 15: return "Max Beauty (Feebas)";
                case 16: return "Item & Male";
                case 17: return "Item & Female";
                case 18: return "Level w/ Item Day";
                case 19: return "Level w/ Item Night";
                case 20: return "Level w/ Attack";
                case 21: return "Level w/ Pokémon in Party";
                case 22: return "Level & Male";
                case 23: return "Level & Female";
                case 24: return "Level @ Mt. Coronet";
                case 25: return "Level @ Eterna Forest";
                case 26: return "Level @ Route 217";
                    // There are only 26 types, so if something else, error!!!
            }
            return "????";
        }

        public string GetMoveName(int index)
        {
            if (index < MOVE_NAMES.Length)
                return MOVE_NAMES[index];
            else
                return "????????";
        }
    }

    public struct BaseStats
    {
        public byte HP, Attack, Defense, Speed, SpAttack, SpDefense;
        public byte Type1, Type2;
        public byte CatchRate, BaseXp;
        public ushort EffortYield;//1, EffortYield2;
        public ushort Item1, Item2;
        public byte GenderRatio;
        public byte EggCycles; // - 1
        public byte BaseHappiness;
        public byte XpType;
        public byte EggType1, EggType2;
        public byte Ability1, Ability2;
        public byte SafariRunRate;
        public byte DexColor;
        //ushort Padding;
        public byte[] TMSwitches; // 13 bytes
        //ushort Padding;
        //byte Padding;

        public byte EVHp, EVAttack, EVDefense, EVSpeed, EVSpAttack, EVSpDefense;
        public bool HasGenders;
    }

    public struct Evolution
    {
        public ushort Type;
        public ushort Parameter;
        public ushort Target;
    }

    public struct Move
    {
        public ushort Value;
        public ushort Level;
    }
}
